package online.foxnull2.foxmall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import online.foxnull2.common.constant.CartConstant;
import online.foxnull2.common.utils.R;
import online.foxnull2.foxmall.cart.feign.ProductFeignService;
import online.foxnull2.foxmall.cart.interceptor.CartInterceptor;
import online.foxnull2.foxmall.cart.service.CartService;
import online.foxnull2.foxmall.cart.to.SkuInfoTo;
import online.foxnull2.foxmall.cart.to.UserInfoTo;
import online.foxnull2.foxmall.cart.vo.cart.CartItem;
import online.foxnull2.foxmall.cart.vo.cart.CartVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Slf4j
@Service("cartServiceImpl")
public class CartServiceImpl implements CartService {

    private final StringRedisTemplate stringRedisTemplate;

    private final ProductFeignService productFeignService;

    private final ThreadPoolExecutor executor;

    @Autowired
    public CartServiceImpl(StringRedisTemplate stringRedisTemplate, ProductFeignService productFeignService, ThreadPoolExecutor executor) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.productFeignService = productFeignService;
        this.executor = executor;
    }

    @Override
    public void addToCart(Long skuId, Integer num) {

        BoundHashOperations<String, Object, Object> operations = getCartOperations();
        Object sku = operations.get(skuId.toString());
        if (ObjectUtils.isEmpty(sku)) {
            CartItem cartItem = new CartItem();
            CompletableFuture<Void> skuInfoFuture = CompletableFuture.runAsync(() -> {
                R r = productFeignService.info(skuId);
                SkuInfoTo skuInfoTo = r.getObj(new TypeReference<>() {
                });
                cartItem.setSkuId(skuId)
                        .setTitle(skuInfoTo.getSkuTitle())
                        .setDefaultImg(skuInfoTo.getSkuDefaultImg())
                        .setPrice(skuInfoTo.getPrice())
                        .setCount(num);
            }, executor);
            CompletableFuture<Void> skuSaleFuture = CompletableFuture.runAsync(() -> {
                List<String> skuSaleValues = productFeignService.listSkuSaleValueBySkuId(skuId);
                cartItem.setSkuSaleAttr(skuSaleValues);
            }, executor);
            try {
                CompletableFuture.allOf(skuInfoFuture, skuSaleFuture).get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
            String jsonString = JSON.toJSONString(cartItem);
            operations.put(skuId.toString(), jsonString);
        } else {
            CartItem cartItem = JSONObject.parseObject(sku.toString(), CartItem.class);
            cartItem.setCount(cartItem.getCount() + num);
            String jsonString = JSON.toJSONString(cartItem);
            operations.put(skuId.toString(), jsonString);
        }
    }

    @Override
    public CartItem getCartItemBySkuId(Long skuId) {
        BoundHashOperations<String, Object, Object> operations = getCartOperations();
        Object sku = operations.get(skuId.toString());
        if (!ObjectUtils.isEmpty(sku)) {
            return JSONObject.parseObject(sku.toString(), CartItem.class);
        }
        return null;
    }

    @Override
    public CartVo getCart() {

        CartVo cartVo = new CartVo();
        ThreadLocal<UserInfoTo> threadLocal = CartInterceptor.threadLocal;
        UserInfoTo userInfoTo = threadLocal.get();
        if (!ObjectUtils.isEmpty(userInfoTo.getUserId())) {
            List<CartItem> tempCartItems = listCartItemByCartKey(CartConstant.CART_PREFIX + userInfoTo.getUserKey());
            if (!ObjectUtils.isEmpty(tempCartItems)) {
                tempCartItems.forEach(cartItem -> this.addToCart(cartItem.getSkuId(), cartItem.getCount()));
                this.clearCartByCartKey(CartConstant.CART_PREFIX + userInfoTo.getUserKey());
            }
            List<CartItem> cartItems = listCartItemByCartKey(CartConstant.CART_PREFIX + userInfoTo.getUserId());
            cartVo.setItems(cartItems);
        } else {
            List<CartItem> cartItems = listCartItemByCartKey(CartConstant.CART_PREFIX + userInfoTo.getUserKey());
            cartVo.setItems(cartItems);
        }
        log.debug("getCart -> {}", cartVo);

        return cartVo;
    }

    @Override
    public void clearCartByCartKey(String cartKey) {
        stringRedisTemplate.delete(cartKey);
    }

    @Override
    public void checkItem(Long skuId, Integer checked) {
        CartItem cartItem = getCartItemBySkuId(skuId);
        cartItem.setCheck(checked == 1);
        BoundHashOperations<String, Object, Object> operations = getCartOperations();
        String jsonString = JSONObject.toJSONString(cartItem);
        operations.put(skuId.toString(), jsonString);
    }

    @Override
    public void countItem(Long skuId, Integer count) {
        CartItem cartItem = getCartItemBySkuId(skuId);
        cartItem.setCount(count);
        BoundHashOperations<String, Object, Object> operations = getCartOperations();
        String jsonString = JSONObject.toJSONString(cartItem);
        operations.put(skuId.toString(), jsonString);
    }

    @Override
    public void removeItem(Long skuId) {
        BoundHashOperations<String, Object, Object> operations = getCartOperations();
        operations.delete(skuId.toString());
    }

    @Override
    public List<CartItem> listCartItem() {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        if (!ObjectUtils.isEmpty(userInfoTo.getUserId())) {
            List<CartItem> cartItems = listCartItemByCartKey(CartConstant.CART_PREFIX + userInfoTo.getUserId());
            if (!ObjectUtils.isEmpty(cartItems)) {
                List<CartItem> collect = cartItems.stream()
                        .filter(CartItem::getCheck)
                        .peek(cartItem -> {
                            BigDecimal price = productFeignService.getPriceBySkuId(cartItem.getSkuId());
                            cartItem.setPrice(price);
                        })
                        .collect(Collectors.toList());
                log.debug("listCartItem -> {}", collect);

                return collect;
            }
        }
        return null;
    }

    private BoundHashOperations<String, Object, Object> getCartOperations() {

        ThreadLocal<UserInfoTo> threadLocal = CartInterceptor.threadLocal;
        UserInfoTo userInfoTo = threadLocal.get();
        String cartKey = CartConstant.CART_PREFIX;
        if (!ObjectUtils.isEmpty(userInfoTo.getUserId())) {
            cartKey += userInfoTo.getUserId().toString();
        } else {
            cartKey += userInfoTo.getUserKey();
        }

        return stringRedisTemplate.boundHashOps(cartKey);
    }

    private List<CartItem> listCartItemByCartKey(String cartKey) {
        BoundHashOperations<String, Object, Object> operations = stringRedisTemplate.boundHashOps(cartKey);
        List<Object> values = operations.values();
        if (!ObjectUtils.isEmpty(values)) {
            List<CartItem> collect = values.stream().map(
                            sku -> JSONObject.parseObject(sku.toString(), CartItem.class))
                    .collect(Collectors.toList());
            log.debug("listCartItemByCartKey " + cartKey + " -> {}", collect);
            return collect;
        }
        return null;
    }
}
